GORM v2操作MySQL

多看官网!

在之前的项目中,都是使用gorm v1来操作MySQL(https://v1.gorm.io/)。

现在来学习使用一下gorm v2操作MySQL。

GORM 2.0 发布说明

GORM 2.0 完全从零开始,引入了一些不兼容的 API 变更和许多改进。

简单记录gorm v2

在正式开始之前,先简单记录一下gorm v2的功能。

字段级权限控制

可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段

1
2
3
4
5
6
7
8
9
10
11
12
type User struct {
Name string `gorm:"<-:create"` // allow read and create
Name string `gorm:"<-:update"` // allow read and update
Name string `gorm:"<-"` // allow read and write (create and update)
Name string `gorm:"<-:false"` // allow read, disable write permission
Name string `gorm:"->"` // readonly (disable write permission unless it configured)
Name string `gorm:"->;<-:create"` // allow read and create
Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
Name string `gorm:"-"` // ignore this field when write and read with struct
Name string `gorm:"-:all"` // ignore this field when write, read and migrate with struct
Name string `gorm:"-:migration"` // ignore this field when migrate with struct
}

如果在自动迁移时忽略某个字段:

1
Name string `gorm:"-:migration"`

这里以后可能会比较常用。

字段标签

gorm struct tag

声明 model 时,tag 是可选的, tag 名大小写不敏感,但建议使用 camelCase 风格。

GORM 支持以下 tag:

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
serializer specifies serializer for how to serialize and deserialize data into db, e.g: serializer:json/gob/unixtime
size specifies column data size/length, e.g: size:256
primaryKey specifies column as primary key
unique specifies column as unique
default specifies column default value
precision specifies column precision
scale specifies column scale
not null specifies column as NOT NULL
autoIncrement specifies column auto incrementable
autoIncrementIncrement auto increment step, controls the interval between successive column values
embedded embed the field
embeddedPrefix column name prefix for embedded fields
autoCreateTime track current time when creating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoCreateTime:nano
autoUpdateTime track current time when creating/updating, for int fields, it will track unix seconds, use value nano/milli to track unix nano/milli seconds, e.g: autoUpdateTime:milli
index create index with options, use same name for multiple fields creates composite indexes, refer Indexes for details
uniqueIndex same as index, but create uniqued index
check creates check constraint, eg: check:age > 13, refer Constraints
<- set field’s write permission, <-:create create-only field, <-:update update-only field, <-:false no write permission, <- create and update permission
-> set field’s read permission, ->:false no read permission
- ignore this field, - no read/write permission, -:migration no migrate permission, -:all no read/write/migrate permission
comment add comment for field when migration

连接数据库

GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。

这里我们以MySQL为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"log"
"os"
"time"

logging "github.com/sirupsen/logrus"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)

func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
TablePrefix: "", // 表前缀
SingularTable: true, // 如果设置禁用表名复数形式属性为 true,`User` 的表名将是 `user`(因为gorm默认表名是复数)
},
//Logger: logger.Default(),
Logger: logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: logger.Info, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
Colorful: false, // 禁用彩色打印
}),
})
MysqlDB, err := db.DB()
if err != nil {
logging.Info(err)
}
MysqlDB.SetMaxIdleConns(10) // 设置空闲连接池中连接的最大数量
MysqlDB.SetMaxOpenConns(100) // 设置打开数据库连接的最大数量
MysqlDB.SetConnMaxLifetime(30 * time.Second) // 设置了连接可复用的最大时间
}

MySQL 驱动程序提供了 一些高级配置 可以在初始化过程中使用,例如:

1
2
3
4
5
6
7
8
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
}), &gorm.Config{})

CRUD接口

CRUD接口

具体使用多看看官网就好了。这里就给点代码简单实践一下。

关于gorm v2 logger LogLevel的设置,结合gin可以这么写:

1
2
3
4
5
6
var mysqlLogger logger.Interface
if gin.Mode() == "debug" {
mysqlLogger = logger.Default.LogMode(logger.Info)
} else {
mysqlLogger = logger.Default
}

Gin Mode的选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package t_test

import (
"fmt"
"testing"
"time"

logging "github.com/sirupsen/logrus"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)

var MysqlDB *gorm.DB // 全局MysqlDB

type User struct {
gorm.Model
Name string
Major string
}

func init() {
// mysqlLogger := logger.Default 默认LogLevel为Warn,一般我们开发调试时LogLevel设置为Info
mysqlLogger := logger.Default.LogMode(logger.Info)
dsn := "root:123456@tcp(127.0.0.1:3308)/test?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: dsn, // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用datetime精度,MySQL5.6之前的数据库不支
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL5.7之前的数据库和MariaDB不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL8之前的数据库和MariaDB不支持重命名列
SkipInitializeWithVersion: false, // 根据当前MySQL版本自动配置
}), &gorm.Config{
Logger: mysqlLogger,
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 表名不加s
//TablePrefix: "test_", // 指定表名前缀为test_
},
})
if err != nil {
logging.Info(err)
}
sqlDB, err := db.DB()
if err != nil {
logging.Info(err)
}
sqlDB.SetMaxIdleConns(20) // 设置空闲连接池中的最大连接数
sqlDB.SetMaxOpenConns(100) // 设置数据库连接最大打开数
sqlDB.SetConnMaxLifetime(time.Second * 30) // 设置可重用连接的最长时间

err = db.AutoMigrate(&User{}) // 自动迁移
if err != nil {
logging.Info(err)
}

MysqlDB = db
}

func TestCreate(t *testing.T) {
user := User{
Name: "张三",
Major: "计算机科学技术",
}
err := MysqlDB.Create(&user).Error
if err != nil {
logging.Info(err)
}
}

func TestSelect(t *testing.T) {
var user User
name := "张三"
err := MysqlDB.Where("name = ?", name).First(&user).Error
if err != nil {
logging.Info(err)
}
fmt.Println(user)
}

func TestUpdate(t *testing.T) {
name := "张三"
err := MysqlDB.Model(&User{}).Where("name = ?", name).Updates(User{Major: "软件工程"}).Error
if err != nil {
logging.Info(err)
}
}

func TestDelete(t *testing.T) {
name := "张三"
err := MysqlDB.Where("name = ?", name).Delete(&User{}).Error
if err != nil {
logging.Info(err)
}
}

总结gorm v2相较于v1的变化

更多的区别,参考:GORM 2.0 发布说明

import驱动不同

  • gorm v1 import:

    1
    2
    3
    4
    import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
    )
  • gorm v2 import:

    1
    2
    3
    4
    import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    )

数据库连接方式不同

gorm v1和gorm v2对比

以MySQL为例,gorm v1和gorm v2对数据库的连接有比较大的差异。

  • gorm v1:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    var MysqlDB *gorm.DB // 全局MysqlDB

    func Init() {
    //dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
    var builder strings.Builder
    s := []string{config.MysqlUser, ":", config.MysqlPassword, "@tcp(", config.MysqlHost, ":", config.MysqlPort, ")/", config.MysqlName, "?charset=utf8mb4&parseTime=True&loc=Local"}
    for _, str := range s {
    builder.WriteString(str)
    }
    dsn := builder.String()
    db, err := gorm.Open("mysql", dsn)
    if err != nil {
    log.Infoln(err)
    }
    db.LogMode(config.MysqlIsLog) // 开启 Logger, 以展示详细的日志
    db.SingularTable(config.MysqlIsSingularTable) // 如果设置禁用表名复数形式属性为 true,`User` 的表名将是 `user`(因为gorm默认表名是复数)
    db.DB().SetMaxIdleConns(config.MysqlMaxIdleConns) // 设置空闲连接池中的最大连接数
    db.DB().SetMaxOpenConns(config.MysqlMaxOpenConns) // 设置数据库连接最大打开数。
    db.DB().SetConnMaxLifetime(config.MysqlConnMaxLifetime) // 设置可重用连接的最长时间
    // 自动迁移
    db.AutoMigrate(&model.User{})
    MysqlDB = db
    }
  • gorm v2:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    var MysqlDB *gorm.DB // 全局MysqlDB

    func init() {
    // mysqlLogger := logger.Default 默认LogLevel为Warn,一般我们开发调试时LogLevel设置为Info
    mysqlLogger := logger.Default.LogMode(logger.Info)
    dsn := "root:123456@tcp(127.0.0.1:3308)/test?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.New(mysql.Config{
    DSN: dsn, // DSN data source name
    DefaultStringSize: 256, // string 类型字段的默认长度
    DisableDatetimePrecision: true, // 禁用datetime精度,MySQL5.6之前的数据库不支
    DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL5.7之前的数据库和MariaDB不支持重命名索引
    DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL8之前的数据库和MariaDB不支持重命名列
    SkipInitializeWithVersion: false, // 根据当前MySQL版本自动配置
    }), &gorm.Config{
    Logger: mysqlLogger,
    NamingStrategy: schema.NamingStrategy{
    SingularTable: true, // 表名不加s
    //TablePrefix: "test_", // 指定表名前缀为test_
    },
    })
    if err != nil {
    logging.Info(err)
    }
    sqlDB, err := db.DB()
    if err != nil {
    logging.Info(err)
    }
    sqlDB.SetMaxIdleConns(20) // 设置空闲连接池中的最大连接数
    sqlDB.SetMaxOpenConns(100) // 设置数据库连接最大打开数
    sqlDB.SetConnMaxLifetime(time.Second * 30) // 设置可重用连接的最长时间

    err = db.AutoMigrate(&User{}) // 自动迁移
    if err != nil {
    logging.Info(err)
    }

    MysqlDB = db
    }

关于gorm v2 logger LogLevel的设置,结合gin可以这么写:

1
2
3
4
5
6
var mysqlLogger logger.Interface
if gin.Mode() == "debug" {
mysqlLogger = logger.Default.LogMode(logger.Info)
} else {
mysqlLogger = logger.Default
}

Gin Mode的选择

读写分离

关于GORM v2 的一些改变